Decorators

Introduction

A decorator is the name used for a software design pattern. Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.

Python decorator is a specific change to the Python syntax that allows us to more conveniently alter functions and methods (and possibly classes in a future version). This supports more readable applications of the DecoratorPattern but also other uses as well.


In [32]:
def bread(test_funct):
    def hyderabad():
        print("</''''''\>")
        test_funct()
        print("<\______/>")
    return hyderabad

def ingredients(test_funct):
    def chennai():
        print("#tomatoes#")
        test_funct()
        print("~salad~")
    return chennai

def cheese(food="--Say Cheese--"):
    print(food)

In [6]:
ch = bread(test_funct=cheese)

In [7]:
ch()


</''''''\>
--Say Cheese--
<\______/>

In [8]:
inn = bread(ingredients(cheese))
inn()


</''''''\>
#tomatoes#
--Say Cheese--
~salad~
<\______/>

Function Decorators

A function decorator is applied to a function definition by placing it on the line before that function definition begins


In [9]:
@bread
@ingredients
def sandwich(food="--Say Cheese--"):
    print(food)

sandwich()


</''''''\>
#tomatoes#
--Say Cheese--
~salad~
<\______/>

!!! Order Matters !!!


In [10]:
@ingredients
@bread
def sandwich(food="--Say Cheese--"):
    print(food)

sandwich()


#tomatoes#
</''''''\>
--Say Cheese--
<\______/>
~salad~

In [10]:
@bread
@ingredients
def hotdog(food="Jam"):
    print(food)

hotdog()


</''''''\>
#tomatoes#
Jam
~salad~
<\______/>

In [17]:
def diet_sandwitch(inner_func):
    def inner():
        print("salad")
    return inner

In [28]:
@ingredients
@diet_sandwitch
def sandwich(food="--Say Cheese--"):
    print(food)

sandwich()


#tomatoes#
salad
~salad~

Decorators with arguments

Using Functions

We can create a function which can take an argument and


In [12]:
def Names(test_funct):
    def inner():
        print("{Hello}")
        print("\tA-Priya")
        print("\tManish Gupta")
        print("\tNeha", end="\n\t")
        test_funct()
        print("(/Hello}")
    return inner

@Names
def print_AShanti():
    print("A-Shanti")

print_AShanti()


{Hello}
	A-Priya
	Manish Gupta
	Neha
	A-Shanti
(/Hello}

In [35]:



	 Mayank
A-Shanti
{Hello}
	A-Priya
	Manish Gupta
	Neha
	
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-35-3befc2bde045> in <module>()
     24     print("A-Shanti")
     25 
---> 26 print_AShanti()

<ipython-input-35-3befc2bde045> in inner()
     14         print("\tManish Gupta")
     15         print("\tNeha", end="\n\t")
---> 16         test_funct()
     17         print("(/Hello}")
     18     return inner

TypeError: 'NoneType' object is not callable

In [ ]:

Class Decorators


In [ ]:

Bound methods


Unless you tell it not to, Python will create what is called a bound method when a function is an attribute of a class and you access it via an instance of a class. This may sound complicated but it does exactly what you want.


In [22]:
class A(object):
    def method(*argv):
        return argv
a = A()
a.method


Out[22]:
<bound method A.method of <__main__.A object at 0x00000234D70DB8D0>>

In [23]:
a.method('an arg')


Out[23]:
(<__main__.A at 0x234d70db8d0>, 'an arg')

staticmethod()

A static method is a way of suppressing the creation of a bound method when accessing a function.


In [24]:
class A(object):
    @staticmethod
    def method(*argv):
        return argv
a = A()
a.method


Out[24]:
<function __main__.A.method>

When we call a static method we don’t get any additional arguments.


In [25]:
a.method('an arg')


Out[25]:
('an arg',)

classmethod

A class method is like a bound method except that the class of the instance is passed as an argument rather than the instance itself.


In [3]:
class A(object):
    @classmethod
    def method(*argv):
        return argv
a = A()
a.method


Out[3]:
<bound method type.method of <class '__main__.A'>>

In [2]:
a.method('an arg')


Out[2]:
(__main__.A, 'an arg')

In [3]:
def test(strg):
    print("Name: ", strg)
    
def hello(func, name):
    print("Ja")
    func(name)
    
hello(test, "Mayank")


Ja
Name:  Mayank

In [4]:
class B(object):
    @classmethod
    def method(*argv):
        return argv

In [5]:
a = B()
a.method()


Out[5]:
(__main__.B,)